Skip to content

feat: goal-based savings tracking & milestones (fixes #133)#643

Open
DrGalio wants to merge 2 commits intorohitdash08:mainfrom
DrGalio:feat/goal-based-savings-tracking
Open

feat: goal-based savings tracking & milestones (fixes #133)#643
DrGalio wants to merge 2 commits intorohitdash08:mainfrom
DrGalio:feat/goal-based-savings-tracking

Conversation

@DrGalio
Copy link

@DrGalio DrGalio commented Mar 24, 2026

Summary

Implements a full goal-based savings tracking system with contribution logging and milestone detection — addressing Issue #133 ($250 bounty).

What's Built

Models (models.py)

  • SavingsGoal: name, target_amount, current_amount, currency, deadline, category, status (ACTIVE/COMPLETED/PAUSED)
  • SavingsContribution: individual contributions with amount, note, date — linked to goal with cascade delete
  • SavingsGoalStatus enum for type-safe status values

API Endpoints (routes/savings.py)

Endpoint Description
GET /savings/goals List goals with progress %, remaining, milestones reached. Filter by ?status=
POST /savings/goals Create goal (name, target_amount, currency, deadline, category)
GET /savings/goals/:id Goal detail with full contribution history
PATCH /savings/goals/:id Update name, target, deadline, category, or status
DELETE /savings/goals/:id Delete goal + all contributions (cascade)
POST /savings/goals/:id/contribute Add contribution — auto-updates current_amount and status
GET /savings/goals/:id/milestones Milestone tracker: which of 25/50/75/100% reached + next milestone + amount needed

Key Features

  • Auto-completion: Goal status automatically flips to COMPLETED when current_amount >= target_amount
  • Milestone tracking: Returns which thresholds (25%, 50%, 75%, 100%) have been reached, plus the next upcoming milestone and how much is needed to reach it
  • Progress analytics: Every goal response includes progress_pct, remaining, and milestones_reached
  • Over-saving allowed: Contributions can exceed the target (progress_pct can go above 100%)
  • User isolation: All endpoints are scoped to the authenticated user — other users cannot see or contribute to your goals

Database

  • savings_goals and savings_contributions tables with proper indexes
  • Migration file for existing deployments: migrations/002_savings_goals.sql
  • Auto-migration via _ensure_schema_compatibility() — zero-downtime deploy

Tests (tests/test_savings.py)

20 tests covering:

  • Goal CRUD (create, list, get, update, delete)
  • Input validation (missing name, zero/negative target)
  • Contributions (single, multiple accumulate correctly, exceed target)
  • Auto-completion at 100%
  • Milestones endpoint (partial progress, all reached)
  • Auth enforcement (401 for unauthenticated)
  • User isolation (other users cannot see or contribute to your goals)

OpenAPI

  • Full documentation for all 7 endpoints
  • SavingsGoal, SavingsGoalDetail, NewSavingsGoal, UpdateSavingsGoal, NewContribution schemas
  • New Savings tag

Acceptance Criteria

  • ✅ Production-ready implementation
  • ✅ Includes tests (20 tests, full CRUD + milestones + auth + isolation)
  • ✅ Documentation updated (OpenAPI, inline docstrings, this PR)

Example Flow

# Create a goal
POST /savings/goals {"name": "Vacation", "target_amount": 1000}
# → { progress_pct: 0, milestones_reached: [] }

# Add contributions
POST /savings/goals/1/contribute {"amount": 250}
# → { progress_pct: 25, milestones_reached: [25] }

POST /savings/goals/1/contribute {"amount": 500}
# → { progress_pct: 75, milestones_reached: [25, 50, 75] }

# Check milestones
GET /savings/goals/1/milestones
# → { next_milestone: 100, amount_to_next: 250 }

# Final push
POST /savings/goals/1/contribute {"amount": 250}
# → { status: "COMPLETED", progress_pct: 100, milestones_reached: [25, 50, 75, 100] }

Files Changed

packages/backend/app/models.py                    — SavingsGoal + SavingsContribution models
packages/backend/app/routes/savings.py             — 7 savings endpoints (NEW)
packages/backend/app/routes/__init__.py            — register savings blueprint
packages/backend/app/__init__.py                   — auto-migration for savings tables
packages/backend/app/db/schema.sql                 — savings_goals + savings_contributions
packages/backend/app/db/migrations/002_savings_goals.sql — migration (NEW)
packages/backend/app/openapi.yaml                  — 7 endpoints + 5 schemas
packages/backend/tests/test_savings.py             — 20 tests (NEW)

8 files changed, 967 insertions

DrGalio added 2 commits March 24, 2026 16:35
)

Implements a production-grade background job execution system with:

Core Engine (services/jobs.py):
- Exponential backoff retries (30s → 2min → 8min)
- Dead-letter queue for permanently failed jobs
- @register_handler decorator for pluggable job types
- process_due_jobs() batch processor with configurable limits
- retry_dead_letter_job() for manual reprocessing
- get_job_stats() for monitoring aggregation

Job Model (models.py):
- BackgroundJob model with full lifecycle tracking
- JobStatus enum: PENDING, RUNNING, SUCCESS, FAILED, DEAD_LETTER
- Tracks: attempt count, max_retries, next_run_at, last_error, result

Reminders Integration (routes/reminders.py):
- run_due endpoint now enqueues jobs instead of fire-and-forget
- send_reminder raises on failure for proper retry handling
- Handler registered via @register_handler decorator

Admin Monitoring API (routes/jobs.py):
- GET /admin/jobs/stats — aggregated stats by status/type
- GET /admin/jobs — paginated list with status/type filters
- GET /admin/jobs/:id — full job details
- POST /admin/jobs/:id/retry — manual dead-letter retry
- POST /admin/jobs/process — manual batch processing trigger
- DELETE /admin/jobs/:id — remove job record
- All endpoints require admin role

Observability (observability.py):
- finmind_job_events_total Prometheus counter (event/job_type/status)
- track_job_event() helper function

Database:
- background_jobs table with indexes (schema.sql)
- Migration file for existing deployments (001_background_jobs.sql)
- Auto-migration via _ensure_schema_compatibility()

OpenAPI (openapi.yaml):
- Full documentation for all 6 admin endpoints
- BackgroundJob schema definition
- Jobs tag added

Tests (tests/test_jobs.py):
- 20 tests covering service layer and admin API
- Backoff calculation, enqueue, execute, retry, dead-letter
- Admin auth enforcement (403 for non-admin, 401 for unauth)
Implements a full savings goal system with contribution tracking and milestone detection:

Models (models.py):
- SavingsGoal: name, target_amount, current_amount, currency, deadline, category, status
- SavingsContribution: goal_id, amount, note, contributed_at
- SavingsGoalStatus enum: ACTIVE, COMPLETED, PAUSED
- Relationship: goal.contributions (dynamic, cascade delete)

Routes (routes/savings.py):
- GET /savings/goals — list goals with ?status= filter, progress_pct, milestones_reached
- POST /savings/goals — create goal with name, target_amount, currency, deadline, category
- GET /savings/goals/:id — goal detail with all contributions
- PATCH /savings/goals/:id — update name, target, deadline, category, status
- DELETE /savings/goals/:id — delete goal + cascade contributions
- POST /savings/goals/:id/contribute — add contribution, auto-update current_amount
- GET /savings/goals/:id/milestones — milestone status at 25/50/75/100%

Features:
- Auto-complete: goal status flips to COMPLETED when current_amount >= target_amount
- Milestone tracking: reports which thresholds reached + next milestone + amount needed
- Progress analytics: progress_pct, remaining amount, milestones_reached list
- User isolation: all endpoints scoped to authenticated user
- Over-saving allowed: contributions can exceed target (progress_pct > 100)

Database:
- savings_goals + savings_contributions tables (schema.sql)
- Migration file (002_savings_goals.sql)
- Auto-migration via _ensure_schema_compatibility()

Tests (tests/test_savings.py) — 20 tests:
- Goal CRUD (create, list, get, update, delete)
- Validation (missing name, zero/negative target)
- Contributions (single, multiple accumulate, exceed target)
- Auto-completion at 100%
- Milestones endpoint (partial, all reached)
- Auth enforcement (401 unauthenticated)
- User isolation (other user cannot see/contribute)

OpenAPI:
- Full docs for all 7 endpoints
- SavingsGoal, SavingsGoalDetail, NewSavingsGoal, UpdateSavingsGoal, NewContribution schemas
- Savings tag

8 files changed, 607 insertions
@DrGalio DrGalio requested a review from rohitdash08 as a code owner March 24, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant